#include "aescrypto.h"
#include "base64.h"
#include <openssl/aes.h>
#include <QCryptographicHash>
#include <QDebug>

AesCrypto::AesCrypto(Algorithm algorithm, QByteArray key, QObject *parent) : QObject(parent)
{
    switch(algorithm) {
    case AES_ECB_128:
    case AES_CBC_128:
    case AES_CFB_128:
    case AES_OFB_128:
    case AES_CTR_128:
        // 判断密钥长度是否对应
        assert(key.size() == 16);
        break;

    case AES_ECB_192:
    case AES_CBC_192:
    case AES_CFB_192:
    case AES_OFB_192:
    case AES_CTR_192:
        // 判断密钥长度是否对应
        assert(key.size() == 24);
        break;

    case AES_ECB_256:
    case AES_CBC_256:
    case AES_CFB_256:
    case AES_OFB_256:
    case AES_CTR_256:
        // 判断密钥长度是否对应
        assert(key.size() == 32);
        break;
    }

    m_type = algorithm;
    m_key = key;
}

// 加密
QByteArray AesCrypto::encrypt(QByteArray text)
{
    return aesCrypto(text, ENCRYPTO);
}

// 解密
QByteArray AesCrypto::decrypt(QByteArray text)
{
    return aesCrypto(text, DECRYPTO);
}

// 加密解密（加密解密过程相同）的内部函数
QByteArray AesCrypto::aesCrypto(QByteArray text, CryptoType type)
{
    // 将Base64的密文进行转换

    if (type == CryptoType::DECRYPTO) {
        // qDebug() << "待转换的Base64编码的密文大小:" << text.size();

        // text = QByteArray::fromBase64(text);
        Base64 base;
        text = base.decode(text);
        // qDebug() << "将Base64编码转换为密文:" << text.data();
        // qDebug() << "将Base64编码转换为密文的密文大小:" << text.size();
    }



    EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
    assert(ctx);

    unsigned char ivec[AES_BLOCK_SIZE];
    generateIvec(ivec);
    // 初始化加密或解密操作的上下文
    int ret = EVP_CipherInit_ex(ctx, m_algorithms.value(m_type)(), NULL, reinterpret_cast<unsigned char*>(m_key.data()),
                                ivec, type);
    assert(ret);

    // 准备存储数据的内存
    int length = text.size() + 1;
    if (length % AES_BLOCK_SIZE != 0) {
        // 未满足16个字节的整数倍，则需要填充为16个字节的整数倍
        length = (length / AES_BLOCK_SIZE + 1) * AES_BLOCK_SIZE;
    }

    QByteArray output;
    output.reserve(length);

    // 获取原始指针以便配合OpenSSL API的调用
    unsigned char* buffer = reinterpret_cast<unsigned char*>(output.data());
    int outLen = 0, totalLen = 0;
    // 用于对数据进行分块处理，并在每个块的加密或解密过程中更新输出缓冲区
    ret = EVP_CipherUpdate(ctx, buffer, &outLen, reinterpret_cast<const unsigned char*>(text.data()), text.size());
    assert(ret);
    totalLen += outLen;
    // 根据加密结果动态调整大小
    output.resize(totalLen);

    // 用于处理最后一个数据块，并输出加密或解密的最终结果
    ret = EVP_CipherFinal_ex(ctx, buffer + totalLen, &outLen);
    assert(ret);
    totalLen += outLen;
    // 再次根据加密结果动态调整大小
    output.resize(totalLen);


    // 将密文进行Base64编码
    if (type == CryptoType::ENCRYPTO) {
        // 进行Base64格式的转换
        //  qDebug() << "只经过AES加密的密文:" << output.data();
        // qDebug() << "只经过AES加密的密文大小:" << output.size();

        Base64 base;
        output = base.encode(reinterpret_cast<char*>(output.data()), output.size());
        // output = output.toBase64();

        // qDebug() << "经过Base64编码转换之后的密文大小:" << output.size();
    }

    EVP_CIPHER_CTX_free(ctx);

    return output;
}

// 生成初始化向量ivec
void AesCrypto::generateIvec(unsigned char *ivec)
{
    // 将密钥m_key用MD5算法生成一个哈希值散列值，以此来初始化初始化向量ivec
    // MD5算法生成的哈希散列值长度是固定的128位 即: 16个字节
    QCryptographicHash hs(QCryptographicHash::Md5); // 使用MD5算法
    hs.addData(m_key);

    std::string res = hs.result().toStdString();
    for (int i = 0; i < AES_BLOCK_SIZE; ++ i) {
        ivec[i] = res.at(i);
    }
}
